package com.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.meta.Column;
import cn.hutool.json.JSONUtil;
import com.entity.generate.FkBean;
import com.entity.generate.Table;
import com.entity.generate.TypeUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.nio.charset.Charset;
import java.util.*;

/**
 * @ClassName GeneratorUtils
 * @Description TODO
 * @Author 谭庆伟
 * @Date 2020/8/22 14:24
 * @Version 1.0
 **/
@SuppressWarnings("ALL")
public class Generator {
    private String className = "";
    private String camelCaseClassName = "";//小驼峰
    private String tableName = "";
    private String tableComment = "";
    private String columns = "";
    private Collection<Column>  tableColumns = null;
    private String pkName = "";
    private List<FkBean> fkList = new ArrayList<>();
    private String tableWhere = "";
    private String tableIf = "";
    private String tableMapIf = "";
    private String noKeyColumns = "";
    private String noKeyColumnsVal = "";
    private String updateIf = "";
    private String queryAllJoin ="";
    private String queryAll ="";
    private String resourcesPath;
    private String writePath;
    private String dbName;
    @Autowired
    private MetaUtil metaUtil;

    public Generator(String dbName,String tableName,String resourcesPath,String writePath) {

        Table table = metaUtil.getTableMeta(dbName,tableName);
        this.tableName = tableName;
        this.dbName = dbName;
        String className = StrUtil.toCamelCase("_" + tableName);
        String camelCaseClassName = StrUtil.toCamelCase( tableName);
        this.className = className;
        this.camelCaseClassName = camelCaseClassName;
        this.tableColumns = table.getColumns();
        this.tableComment = table.getComment();
        Set<String> pkNames = table.getPkNames();
        this.fkList = table.getFkList();

        this.tableWhere =getColumnWhere(tableName);
        this.tableIf = getColumnIf(tableName);
        this.tableMapIf = getColumnIf(tableName);
        this.pkName = pkNames.iterator().next();
        this.noKeyColumns = getNoKeyColumns();
        this.noKeyColumnsVal = getNoKeyColumnsVal();
        this.columns = getTableColumnAlias(tableName);
        this.updateIf = getUpdateIf();
        this.queryAll = getQueryAll();
        if(this.fkList.size()>0){
            this.queryAllJoin = getQueryAllJoin();
        }
        this.resourcesPath = resourcesPath;
        this.writePath = writePath;
    }

    @Override
    public String toString() {
        return "Generator{" +
                "className='" + className + '\'' +
                ", tableName='" + tableName + '\'' +
                ", tableComment='" + tableComment + '\'' +
                ", columns='" + columns + '\'' +
                ", tableColumns=" + tableColumns +
                ", pkName='" + pkName + '\'' +
                ", fkList=" + fkList +
                ", tableWhere='" + tableWhere + '\'' +
                ", tableIf='" + tableIf + '\'' +
                ", tableMapIf='" + tableMapIf + '\'' +
                ", noKeyColumns='" + noKeyColumns + '\'' +
                ", noKeyColumnsVal='" + noKeyColumnsVal + '\'' +
                ", updateIf='" + updateIf + '\'' +
                ", queryAllJoin='" + queryAllJoin + '\'' +
                ", queryAll='" + queryAll + '\'' +
                '}';
    }

    private String getQueryAll() {
        //拼接sql语句
        //select * from tableName left join
        StrBuilder allColumn = new StrBuilder();
        StrBuilder allWhere = new StrBuilder();
        allWhere.append(" <where>");
        allWhere.append(this.tableIf);
        allWhere.append(" </where>");
        //String columnsStr = allColumn.del(allColumn.length() - 1, allColumn.length()).toString();
        String res = " select " + columns+" from " + tableName  + allWhere;
        return res;
    }

    private String getQueryAllJoin() {
        //拼接sql语句
        //select * from tableName left join
        StrBuilder allColumn = new StrBuilder();
        allColumn.append(this.columns+",");
        StrBuilder allLeftJoin = new StrBuilder();
        StrBuilder allWhere = new StrBuilder();
        StrBuilder allIf = new StrBuilder();
        allWhere.append("<where>");
        for (int i = 0; i < fkList.size(); i++) {
            FkBean fkBean = fkList.get(i);
            String referencesTableName = fkBean.referencesTableName;
            Generator generator = new Generator(dbName,referencesTableName,resourcesPath,writePath);
            String referencesColumnName = fkBean.getReferencesColumnName();
            String fkColumnName = fkBean.getFkColumnName();
            allColumn.append(generator.columns+",");

            System.out.println("allColumn:" + allColumn);
            allIf.append(generator.tableMapIf);
            allLeftJoin.append(" left join " + referencesTableName + " on " + referencesTableName + "." + referencesColumnName + " = " + tableName + "." + fkColumnName);
        }
        allWhere.append(allIf);
        allWhere.append("</where>");
        String columnsStr = allColumn.del(allColumn.length() - 1, allColumn.length()).toString();
        System.out.println("columnsStr:" + columnsStr);
        String res = "select " + columnsStr+" from " + tableName + allLeftJoin + allWhere;
        System.out.println(res);
        return res;
    }

    private String getUpdateIf() {
        StrBuilder builder = new StrBuilder();
        String camelCase = StrUtil.toCamelCase(tableName);
        for (Column column : this.tableColumns) {
            StrBuilder ifBuilder = new StrBuilder();
            String name = column.getName();
            String tcName = StrUtil.toCamelCase(tableName + "_" + name);//表名加列名
            ifBuilder.append("<if test=\""+tcName+" != null  and "+tcName+" != ''\">");
            ifBuilder.append(" "+name+" = #{"+tcName+"}");
            ifBuilder.append("</if>");
            builder.append(ifBuilder);
        }
        return builder.toString();
    }

    public void createController() {

        String entityPath = resourcesPath + "controller" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        map.put("camelCaseClassName", camelCaseClassName);
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), writePath + "controller\\" + className + "Controller.java", Charset.defaultCharset());

    }
    public void createService() {

        String entityPath = resourcesPath + "service" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        String className = StrUtil.toCamelCase("_" + tableName);
        map.put("className", className);
        map.put("context", columnsGetSet(tableName, this.tableColumns, fkList, pkName));
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), writePath + "service\\" + className + "Service.java", Charset.defaultCharset());

    }
    public void createServiceImpl() {

        String entityPath = resourcesPath + "serviceImpl" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        String className = StrUtil.toCamelCase("_" + tableName);
        map.put("className", className);
        map.put("camelCaseClassName",camelCaseClassName );
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), writePath + "service\\impl\\" + className + "ServiceImpl.java", Charset.defaultCharset());

    }

    public void createEntity() {

        String entityPath = resourcesPath + "entity" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        String className = StrUtil.toCamelCase("_" + tableName);
        map.put("className", className);
        map.put("context", columnsGetSet(tableName, this.tableColumns, fkList, pkName));
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), writePath + "entity\\" + className + ".java", Charset.defaultCharset());

    }

    public void createDao() {

        String entityPath = resourcesPath + "dao" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        String format = StrUtil.format(templetaEntityStr, map);
        FileUtil.writeString(JavaForm.formJava(format), writePath + "dao\\" + className + "Dao.java", Charset.defaultCharset());

    }

    public void createIndexJs() {
        String entityPath = resourcesPath + "indexJs" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(writePath, 1) + "\\resources\\public\\vue-antd-admin\\src\\pages\\"+className+"\\";
        FileUtil.writeString(HtmlFormat.format(format), path+ camelCaseClassName + "index.js", Charset.defaultCharset());
    }

    public void createAdd() {
        String entityPath = resourcesPath + "add" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        String addItems = getAddItems();
        map.put("addItems", addItems);
        map.put("classNameJson", getAddClassNameJson());
        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(writePath, 1) + "\\resources\\public\\vue-antd-admin\\src\\pages\\"+className+"\\";
        FileUtil.writeString(HtmlFormat.format(format), path+ camelCaseClassName + "Add.vue", Charset.defaultCharset());
    }

    public void createEdit() {
        String entityPath = resourcesPath + "edit" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(writePath, 1) + "\\resources\\public\\vue-antd-admin\\src\\pages\\"+className+"\\";
        FileUtil.writeString(HtmlFormat.format(format), path+ camelCaseClassName + "Edit.vue", Charset.defaultCharset());
    }

    public void createIndex() {
        String entityPath = resourcesPath + "index" + ".txt";
        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();
        map.put("className", className);
        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(writePath, 1) + "\\resources\\public\\vue-antd-admin\\src\\pages\\"+className+"\\";
        FileUtil.writeString(HtmlFormat.format(format), path+ camelCaseClassName + "Index.vue", Charset.defaultCharset());
    }

    private String getAddClassNameJson() {

        StrBuilder builder = new StrBuilder();
        builder.append("{");
        for (Column tableColumn : tableColumns) {
            String columnName = tableColumn.getName();
            columnName = StrUtil.toCamelCase(columnName);
            int type = tableColumn.getType();
            String javaType = TypeUtil.getJavaType(type);
            String templeta = ""+columnName+": '"+javaType+"',";
            builder.append(templeta);
        }
        builder.append("}");
        String ret = builder.toString();
        return JSONUtil.formatJsonStr(ret);
    }
    private String getAddItems() {
        StrBuilder builder = new StrBuilder();
        for (Column tableColumn : tableColumns) {
            String columnName = tableColumn.getName();
            columnName = StrUtil.toCamelCase(columnName);
            String templeta = "<a-form-model-item label=\""+tableColumn.getComment()+"\"><a-input v-model=\""+className+"."+ columnName +"\" /></a-form-model-item>";
            builder.append(templeta);
        }

        return builder.toString();
    }

    public void createMapper() {

        String entityPath = resourcesPath + "mapper" + ".txt";

        String templetaEntityStr = FileUtil.readUtf8String(entityPath);
        HashMap<String, Object> map = new HashMap<>();

        map.put("className", className);
        map.put("tableName", tableName);
        map.put("columns", columns);
        map.put("pkName", pkName);
        map.put("queryAllJoin",queryAllJoin );
        map.put("queryAll", queryAll);
        map.put("noKeyColumns", noKeyColumns);
        map.put("noKeyColumnsVal", noKeyColumnsVal);
        map.put("updateIf", updateIf);

        String format = StrUtil.format(templetaEntityStr, map);
        String path = FileUtil.getParent(writePath, 3) + "\\resources\\";
        try {
            FileUtil.writeString(XmlFormat.format(format),path + "mapper\\" + className + "Mapper.xml", Charset.defaultCharset());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public String getNoKeyColumns() {
        StrBuilder builder = new StrBuilder();
        for (Column tableColumn : tableColumns) {
            //将不是主键的列加入进来
            String columnName = tableColumn.getName();
            if(!pkName.equals(columnName)){
                builder.append(columnName+",");
            }

        }
        StrBuilder builder1 = builder.del(builder.length() - 1, builder.length());
        return builder1.toString();
    }

    public String getNoKeyColumnsVal() {
        StrBuilder builder = new StrBuilder();
        for (Column tableColumn : tableColumns) {
            //将不是主键的列加入进来
            String columnName = tableColumn.getName();
            if(!pkName.equals(columnName)){
                builder.append("#{" + columnName+"},");
            }
        }
        StrBuilder builder1 = builder.del(builder.length() - 1, builder.length());
        return builder1.toString();
    }

    private String getSQL() {
        //拼接sql语句
        //select * from tableName left join
        StrBuilder allColumn = new StrBuilder();
        allColumn.append(getTableColumnAlias(tableName));
        StrBuilder allLeftJoin = new StrBuilder();
        StrBuilder allWhere = new StrBuilder();
        StrBuilder allIf = new StrBuilder();
        allWhere.append("<where>");
        for (int i = 0; i < fkList.size(); i++) {
            String referencesTableName = fkList.get(i).referencesTableName;
            String referencesColumnName = fkList.get(i).getReferencesColumnName();
            String fkColumnName = fkList.get(i).getFkColumnName();
            allColumn.append(getTableColumnAlias(referencesTableName));
            allIf.append(getColumnIf(referencesTableName));
            allLeftJoin.append(" left join " + referencesTableName + " on " + referencesTableName + "." + referencesColumnName + " = " + tableName + "." + fkColumnName);
        }
        allWhere.append(allIf);
        allWhere.append("</where>");
        String columnsStr = allColumn.del(allColumn.length() - 1, allColumn.length()).toString();
        String res = "select " + columnsStr+" from " + tableName + allLeftJoin + allWhere;
        System.out.println(res);
        return res;
    }

    private String getColumnWhere(String tableName) {
        StrBuilder allWhere = new StrBuilder();
        allWhere.append("<where>");
        getColumnIf(tableName);
        allWhere.append("</where>");
        return allWhere.toString();
    }


    private String getColumnIf(String tableName) {
        StrBuilder builder = new StrBuilder();
        Collection<Column> columns = this.tableColumns;
        String camelCase = StrUtil.toCamelCase(tableName);
        for (Column column : columns) {
            StrBuilder ifBuilder = new StrBuilder();
            String name = column.getName();
            String tcName = StrUtil.toCamelCase(tableName + "_" + name);//表名加列名
            ifBuilder.append("<if test=\""+tcName+" != null  and "+tcName+" != ''\">");
            ifBuilder.append("and "+tableName+"."+name+" = #{"+tcName+"}");
            ifBuilder.append("</if>");
            builder.append(ifBuilder);
        }
        return builder.toString();
    }

    private String getColumnMapIf(String tableName) {
        StrBuilder builder = new StrBuilder();
        Collection<Column> columns = this.tableColumns;
        String camelCase = StrUtil.toCamelCase(tableName);
        for (Column column : columns) {
            StrBuilder ifBuilder = new StrBuilder();
            String name = column.getName();
            String tcName = StrUtil.toCamelCase(tableName + "_" + name);//表名加列名
            ifBuilder.append("<if test=\"map."+tcName+" != null and map."+tcName+"!=''\">");
            ifBuilder.append("and "+tableName+"."+name+" = #{map."+tcName+"}");
            ifBuilder.append("</if>");
            builder.append(ifBuilder);
        }
        return builder.toString();
    }

    private String getTableColumnAlias(String tableName) {
        StrBuilder builder = new StrBuilder();
        Table tableMeta = metaUtil.getTableMeta(dbName,tableName);
        Collection<Column> columns = tableMeta.getColumns();
        String camelCase = StrUtil.toCamelCase(tableName);
        for (Column column : columns) {
            String name = column.getName();
            String tcName = StrUtil.toCamelCase(tableName + "_" + name);//表名加列名
            builder.append(tableName + "." + name + " " + tcName + ",");
        }
        String columnsStr = builder.del(builder.length() - 1, builder.length()).toString();

        return columnsStr;
    }

    private String columnsGetSet(String tableName, Collection<Column> columns, List<FkBean> fkList, String pkName) {

        StrBuilder builder = new StrBuilder();
        for (Column column : columns) {
            String name = column.getName();
            int type = column.getType();
            String javaType = TypeUtil.getJavaType(type);
            String columnCamelCase = StrUtil.toCamelCase(name);//
            String columnCamelCaseGetSet = StrUtil.toCamelCase("_" + name);//
            builder.append(columnsIn(javaType, columnCamelCase));
            builder.append(columnsSet(javaType, columnCamelCase, columnCamelCaseGetSet));
            builder.append(columnsGet(javaType, columnCamelCase, columnCamelCaseGetSet));
        }
        //有外键则加入外键关联
        if (ObjectUtil.isNotEmpty(fkList)) {
            //一对一
            //String pkName = pkNames.iterator().next();
            //关联关系 1-1为1是一对一 1-3是一对多 3-3是多对多
            String relationship = "";
            for (int i = 0; i < fkList.size(); i++) {
                FkBean fkBean = fkList.get(i);
                String fkColumnName = fkBean.fkColumnName;
                String referencesColumnName = fkBean.getReferencesColumnName();
                String referencesTableName = fkBean.getReferencesTableName();
                String referencesTableNameCamelCase = StrUtil.toCamelCase("_" + referencesTableName);
                String referencesTableNameVar = StrUtil.toCamelCase(referencesTableName);
                if (fkColumnName.equals(pkName)) {
                    //建立1-1关系
                    /*@TableField(exist = false)
                    @OneToOne //一对一默认为立即加载，即@Lazy(false)或此时不标注
                    @JoinColumn(name = "lao_gong_id", referencedColumnName = "man_id")
                     private Man laoGong;*/
                    builder.append("@TableField(exist = false)\n");
                    builder.append("@OneToOne\n");
                    builder.append("@JoinColumn(name = \"" + pkName + "\", referencedColumnName = \"" + referencesColumnName + "\")\n");
                    builder.append("private " + referencesTableNameCamelCase + " " + referencesTableNameVar + ";\n");
                    builder.append(ObjectSet(referencesTableNameCamelCase, referencesTableNameVar, referencesTableNameCamelCase));
                    builder.append(ObjectGet(referencesTableNameCamelCase, referencesTableNameVar, referencesTableNameCamelCase));
                } else {
                    //一对多
                    /*@TableField(exist = false)
                    @OneToMany //一对多默认为延迟加载,即@Lazy/@Lazy(true)/或此时不标注
                    @JoinColumn(name="company_id",referencedColumnName = "company_id")//@TableId与一方相关属性中@TableField名称保持一致时@JoinColumn可省略
                    private Set<Man> employees;*/
                    builder.append("@TableField(exist = false)\n");
                    builder.append("@OneToMany\n");
                    builder.append("@JoinColumn(name=\"" + fkColumnName + "\",referencedColumnName = \"" + referencesColumnName + "\")\n");
                    builder.append("private Set<" + referencesTableNameCamelCase + "> " + referencesTableNameVar + "es;\n");
                    builder.append(ObjectSet("Set<" + referencesTableNameCamelCase + "> ", referencesTableNameVar + "es", referencesTableNameCamelCase));
                    builder.append(ObjectGet("Set<" + referencesTableNameCamelCase + "> ", referencesTableNameVar + "es", referencesTableNameCamelCase));
                    //建立1-3关系
                }

            }
        }

        return builder.toString();
    }

    private String columnsIn(String javaType, String columnCamelCase) {
        StrBuilder builder = new StrBuilder();
        builder.append("private " + javaType + " " + columnCamelCase + ";\n");
        return builder.toString();
    }

    private String columnsSet(String javaType, String columnCamelCase, String columnCamelCaseGetSet) {
        StrBuilder builder = new StrBuilder();
        builder.append("public void set" + columnCamelCaseGetSet + "(" + javaType + " " + columnCamelCase + "){this." + columnCamelCase + "=" + columnCamelCase + ";}\n");
        return builder.toString();
    }

    private String columnsGet(String javaType, String columnCamelCase, String columnCamelCaseGetSet) {
        StrBuilder builder = new StrBuilder();
        builder.append("public " + javaType + " get" + columnCamelCaseGetSet + "(){return this." + columnCamelCase + ";}\n");
        return builder.toString();
    }

    private String ObjectSet(String type, String var, String objectName) {
        StrBuilder builder = new StrBuilder();
        builder.append("public void set" + objectName + "(" + type + " " + var + "){this." + var + "=" + var + ";}\n");
        return builder.toString();
    }

    private String ObjectGet(String type, String var, String objectName) {
        StrBuilder builder = new StrBuilder();
        builder.append("public " + type + " get" + objectName + "(){return this." + var + ";}\n");
        return builder.toString();
    }


}
